#ifndef BOID_H
#define BOID_H

#include "vector"

#include "ngl/Vector.h"
#include "ngl/Colour.h"

#include "MovingObject.h"
#include "ObstacleManager.h"


/// @file Boid.h
/// @brief represent a basic boid, the unit block of a flocking system
/// @author Perseedoss Rajiv
/// @version 1.0
/// @date 15.03.11
/// Revision History :
/// Initial Version 15.03.11
/// @class Boid
/// @brief keep information and boid behaviours together, managed by the flock manager

class Boid : public MovingObject
{
public:
    /// @brief ctor
    /// @param[in] _id a unique int id for the boid
    /// @param[in] _col the colour of the boid
    /// @param[in] _pos the cartesian position vector of the boid
    /// @param[in] _vel the velocity vector of the boid
    Boid(
            const int _id,
            const ngl::Colour _col,
            const ngl::Vector _pos,
            const ngl::Vector _vel
        );

    /// @brief dtor
    ~Boid();

    /// @brief returns the id of the boid
    inline int getId() const { return m_id; }

    /// @brief returns the colour of the boid
    inline ngl::Colour getColour() const { return m_col; }

    /// @brief returns the list of neighbours of the boid
    inline std::vector<Boid*>* getNeighbourList() const { return m_neighbourList; }

    /// @brief returns the count of neighbours of the boid
    inline int getNeighbourCount() const { return m_neighbourList->size(); }

    /// @brief apply the flocking rules and determine a final velocity for the boid
    /// @param[in] _normaliseRules flag to determine whether the velocities should be normalised
    /// @param[in] _separationDistance the minimum separation distance to maintain between neighbours
    /// @param[in] _enableCohesion flag to determine whether cohesion rule is enabled
    /// @param[in] _enableSeparation flag to determine whether separation rule is enabled
    /// @param[in] _enableAlignment flag to determine whether alignment rule is enabled
    /// @param[in] _cohesionWeight the weight of the cohesion velocity
    /// @param[in] _separationWeight the weight of the separation velocity
    /// @param[in] _alignmentWeight the weight of the alignment velocity
    /// @param[in] _enableObstacleAvoidance flag to determine whether obstacle avoidance rule is enabled
    /// @param[in] _obstacleAvoidanceWeight the weight of the obstacle avoidance velocity
    /// @param[in] _enableTendToward flag to determine whether the tend toward rule is enabled
    /// @param[in] _tendPosition the position towards which the boid should move to
    /// @param[in] _tendTowardWeight the weight to influence the tend towards rule
    /// @param[in] _enableExternalForce flag to determine whether an external force is to be applied
    /// @param[in] _externalForce the external force value to be applied
    /// @param[in] _externalForceWeight the weight of influence of the external force
    /// @param[in] _minSpeed the minimum speed to be enforced to the boid's velocity
    /// @param[in] _maxSpeed the maximum speed to be enforced to the boid's velocity
    /// @param[in] _boidScaleFactor the global scale of the boid
    /// @param[in,out] io_obstacleManager the obstacle manager that contains the list of obstacles
    /// @param[in] _enable2DTopView flag to determine whether we are in 2d top view mode
    void planNextMove
                (
                    const bool _normaliseRules,
                    const float _separationDistance,
                    const bool _enableCohesion,
                    const bool _enableSeparation,
                    const bool _enableAlignment,
                    const float _cohesionWeight,
                    const float _separationWeight,
                    const float _alignmentWeight,
                    const bool _enableObstacleAvoidance,
                    const float _obstacleAvoidanceWeight,
                    const bool _enableTendToward,
                    const ngl::Vector _tendPosition,
                    const float _tendTowardWeight,
                    const bool _enableExternalForce,
                    const ngl::Vector _externalForce,
                    const float _externalForceWeight,
                    const float _minSpeed,
                    const float _maxSpeed,
                    const float _boidScaleFactor,
                    ObstacleManager* io_obstacleManager,
                    const bool _enable2DTopView
                );

    /// @brief move the boid by its internal velocity
    void move();

    /// @brief move the boid by a specified velocity
    /// @param[in] _velocity an addiional velocity by which to move the boid
    void move(const ngl::Vector _velocity);

    /// @brief additional flocking rule, enabling the boid to tend towards some position
    /// @param[in] _weight a controllable weight to influence the final velocity
    /// @param[in] _normalise a flag to decide whether the tend towards velocity should be normalised
    /// @param[in] _tendPosition the position to tend toward
    ngl::Vector applyTendToward
                    (
                        const float _weight,
                        const bool _normalise,
                        const ngl::Vector _tendPosition
                    );

    /// @brief add a boid to the list of neighbours of the the boid
    /// @param[in,out] io_neighbour the neighbouring boid to add
    void addNeighbour(Boid* io_neighbour);

    /// @brief clears the list of neighbours of the boid
    void clearNeighbours();


private:
    /// @brief unique identifier of the boid
    int m_id;

    /// @brief colour of the boid
    ngl::Colour m_col;

    /// @brief list of neighbour boids of the boid
    std::vector<Boid*>* m_neighbourList;


private:
    /// @brief limit the velocity of the boid within a range
    /// @param[in] _minSpeed enforce the minumum possible speed of the boid
    /// @param[in] _maxSpeed enforce the maximum possible speed of the boid
    void applyVelocityLimit(
                                const float _minSpeed,
                                const float _maxSpeed
                           );

    /// @brief basic flocking rule, enabling the boids to cohese with each other
    /// @param[in] _weight a controllable weight to influence the final velocity
    /// @param[in] _normalise a flag to decide whether the cohesion velocity should be normalised
    ngl::Vector applyCohesion(
                                const float _weight,
                                const bool _normalise
                              );

    /// @brief basic separation rule, enabling the boids to keep a minumum distance with each other
    /// @param[in] _weight a controllable weight to influence the final velocity
    /// @param[in] _normalise a flag to decide whether the separation velocity should be normalised
    /// @param[in] _separationDistance the minimum distance to be enforced between neighbours
    ngl::Vector applySeparation(
                                const float _weight,
                                const bool _normalise,
                                const float _separationDistance
                                );

    /// @brief basic alignment rule, enabling the boids to head up uniformly
    /// @param[in] _weight a controllable weight to influence the final velocity
    /// @param[in] _normalise a flag to decide whether the alignment velocity should be normalised
    ngl::Vector applyAlignment(
                                const float _weight,
                                const bool _normalise
                               );

    /// @brief additional rule to apply an external force to each boid of the flock
    /// @param[in] _weight a controllable weight to influence the final velocity
    /// @param[in] _normalise a flag to decide whether the external force velocity should be normalised
    /// @param[in] _externalForce the force vector to be applied
    ngl::Vector applyExternalForce(
                                    const float _weight,
                                    const bool _normalise,
                                    const ngl::Vector _externalForce
                                   );

    /// @brief additional rule to enable the avoidance of obstacles
    /// @param[in] _weight a controllable weight to influence the avoidance
    /// @param[in] _normalise a flag to decide whether the avoidance velocity should be normalised
    /// @param[in,out] io_obstacleManager the obstacle manager that stores a list of the obstacles
    /// @param[out] o_isToBeReversed flag that determine whether collision has occured and velocity should be reversed
    /// @param[in] _enable2DTopView flag to determine whether we are in the 2d top view mode
    ngl::Vector applyObstacleAvoidance
                                (
                                    const float _weight,
                                    const bool _normalise,
                                    ObstacleManager* io_obstacleManager,
                                    bool &o_isToBeReversed,
                                    const bool _enable2DTopView
                                );
};

#endif // BOID_H
